package report;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.util.StrUtil;
import entity.Point;
import entity.enums.Angle;
import entity.enums.HikDeviceStatus;
import entity.enums.HikLiftStatus;
import entity.enums.HikMsgType;
import global.Global;
import global.InitCache;
import io.netty.channel.Channel;
import log.LogInfo;
import report.msg.MsgConstructor;

import java.util.Objects;
import java.util.TimerTask;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 报文上报中心
 * 所有报文回复、实时状态上报
 *
 * @author zhouwz
 * @date 2020/11/3 14:11
 */
public class MsgReportCenter {

    private static Channel channel;

    public static void doReport(Channel channel) {
        try {
            MsgReportCenter.channel = channel;
            ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1, ThreadFactoryBuilder.create().setNamePrefix("center-").build());
            // 报文上报处理器
            msgReportSchedule(scheduledThreadPoolExecutor);
            // 状态上报定时器
            reportStatusSchedule(scheduledThreadPoolExecutor);
            // 移动监听器
            moveListener(scheduledThreadPoolExecutor);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 报文上报处理器
     *
     * @param scheduledThreadPoolExecutor 定时器线程
     */
    private static void msgReportSchedule(ScheduledThreadPoolExecutor scheduledThreadPoolExecutor) {
        scheduledThreadPoolExecutor.scheduleWithFixedDelay(new TimerTask() {
            @Override
            public void run() {
                if (InitCache.registered) {
                    while (InitCache.repQueue.peek() != null) {
                        HikMsgType hikMsgType = InitCache.repQueue.poll();
                        LogInfo.threadAndMsg(hikMsgType);
                        switch (Objects.requireNonNull(hikMsgType)) {
                            case REPORT_NODE_SWITCH_RSQ:
                                channel.write(MsgConstructor.issueHeadAndPlaceholder(HikMsgType.REPORT_NODE_SWITCH_RSQ, 4));
                                break;
                            case REPORT_NODE_REQ:
                                channel.write(MsgConstructor.issueHeadAndPlaceholder(HikMsgType.REPORT_NODE_REQ, MsgConstructor.nodeConstructor(), 4));
                                break;
                            case REPORT_DEVICE_STATE_REQ_0:
                                channel.write(MsgConstructor.issueHeadAndStatusBody(HikMsgType.REPORT_DEVICE_STATE_REQ_0));
                                break;
                            case REPORT_DEVICE_STATE_REQ_3:
                                channel.write(MsgConstructor.issueHeadAndStatusBody(HikMsgType.REPORT_DEVICE_STATE_REQ_3));
                                break;
                            case REPORT_DEVICE_STATE_REQ_4:
                                channel.write(MsgConstructor.issueHeadAndStatusBody(HikMsgType.REPORT_DEVICE_STATE_REQ_4));
                                break;
                            case DETECTION_CONTROL_RSQ:
                                channel.write(MsgConstructor.issueHeadAndPlaceholder(HikMsgType.DETECTION_CONTROL_RSQ, 4));
                                break;
                            case ONLY_LOWER_CONTROL_RSQ:
                                InitCache.realtimeStatus.setHikDeviceStatus(HikDeviceStatus.STATUS_3);
                                channel.write(MsgConstructor.issueHeadAndPlaceholder(HikMsgType.ONLY_LOWER_CONTROL_RSQ, 4));
                                break;
                            case PICK_OVERLAY_RECTILINEAR_MOTION_RSQ:
                                channel.write(MsgConstructor.issueHeadAndPlaceholder(HikMsgType.PICK_OVERLAY_RECTILINEAR_MOTION_RSQ, 4));
                                break;
                            case EMPTY_RECTILINEAR_MOTION_RSQ:
                                channel.write(MsgConstructor.issueHeadAndPlaceholder(HikMsgType.EMPTY_RECTILINEAR_MOTION_RSQ, 4));
                                break;
                            default:
                        }
                    }
                    channel.flush();
                } else {
                    LogInfo.threadAndMsg(HikMsgType.REGISTER_REQ);
                    channel.writeAndFlush(MsgConstructor.issueHeadAndPlaceholder(HikMsgType.REGISTER_REQ, 64));
                }
            }
        }, 0L, 100, TimeUnit.MILLISECONDS);
    }


    /**
     * 状态上报定时器
     *
     * @param scheduledThreadPoolExecutor 定时器线程
     */
    private static void reportStatusSchedule(ScheduledThreadPoolExecutor scheduledThreadPoolExecutor) {
        scheduledThreadPoolExecutor.scheduleWithFixedDelay(new TimerTask() {
            @Override
            public void run() {
                // 恢复实时状态(已完成 → 空闲)
                recoverRealtimeStatus();
                InitCache.repQueue.offer(InitCache.realtimeStatus.getHikLiftStatus() != HikLiftStatus.UNKNOWN ? HikMsgType.REPORT_DEVICE_STATE_REQ_4 : HikMsgType.REPORT_DEVICE_STATE_REQ_0);
            }
        }, 0L, 300, TimeUnit.MILLISECONDS);
    }

    /**
     * 记录的上次完成时间戳
     */
    private static Long lastDoneTime;

    /**
     * 处理实时状态为完成时候的监控
     */
    private static void recoverRealtimeStatus() {
        switch (InitCache.realtimeStatus.getHikDeviceStatus()) {
            case STATUS_3:
                if (InitCache.vehicleTargetInfo.getY() == null) {
                    // 目标坐标不存在时候 执行中 (2秒后)→ 已完成
                    resetTimer(HikDeviceStatus.STATUS_2);
                }
                break;
            case STATUS_2:
                // 已完成 (2秒后)→ 空闲
                resetTimer(HikDeviceStatus.STATUS_1);
                break;
            default:
        }
    }

    /**
     * 重置时间表
     * 执行中 (2秒后)→ 已完成
     * 已完成 (2秒后)→ 空闲
     *
     * @param newStatus 新状态
     */
    private static void resetTimer(HikDeviceStatus newStatus) {
        if (lastDoneTime != null) {
            if (System.currentTimeMillis() - lastDoneTime > Global.INSTRUCTION_DONE_DURATION_MILLION) {
                InitCache.realtimeStatus.setHikDeviceStatus(newStatus);
                lastDoneTime = null;
            }
        } else {
            lastDoneTime = System.currentTimeMillis();
        }
    }

    /**
     * 开始移动标记
     */
    static boolean startMove = false;

    /**
     * 移动监听器
     *
     * @param scheduledThreadPoolExecutor 定时器线程
     */
    private static void moveListener(ScheduledThreadPoolExecutor scheduledThreadPoolExecutor) {
        scheduledThreadPoolExecutor.scheduleWithFixedDelay(new TimerTask() {
            @Override
            public void run() {
                if (InitCache.vehicleTargetInfo.getX() != null && InitCache.vehicleTargetInfo.getY() != null) {
                    int xMoveFlag = InitCache.realtimeStatus.getX() - InitCache.vehicleTargetInfo.getX();
                    int yMoveFlag = InitCache.realtimeStatus.getY() - InitCache.vehicleTargetInfo.getY();
                    if (xMoveFlag != 0 || yMoveFlag != 0) {
                        // 标记开始移动
                        startMove = true;
                    }
                    if (startMove) {
                        if (xMoveFlag < 0) {
                            // 往右移动
                            InitCache.realtimeStatus.setAngle(Angle.RIGHT.getCode());
                            InitCache.realtimeStatus.setX(InitCache.realtimeStatus.getX() + Global.MOVE_STEP);
                        }
                        if (xMoveFlag > 0) {
                            // 往左移动
                            InitCache.realtimeStatus.setAngle(Angle.LEFT.getCode());
                            InitCache.realtimeStatus.setX(InitCache.realtimeStatus.getX() - Global.MOVE_STEP);
                        }
                        if (yMoveFlag < 0) {
                            // 往上移动
                            InitCache.realtimeStatus.setAngle(Angle.UP.getCode());
                            InitCache.realtimeStatus.setY(InitCache.realtimeStatus.getY() + Global.MOVE_STEP);
                        }
                        if (yMoveFlag > 0) {
                            // 往下移动
                            InitCache.realtimeStatus.setAngle(Angle.BOTTOM.getCode());
                            InitCache.realtimeStatus.setY(InitCache.realtimeStatus.getY() - Global.MOVE_STEP);
                        }
                        Point point = InitCache.crossPointCache.get(StrUtil.format("{}_{}", InitCache.realtimeStatus.getX(), InitCache.realtimeStatus.getY()));
                        if (point != null) {
                            InitCache.realtimeStatus.setPoint(point);
                            // 途径的点位进行上报
                            InitCache.repQueue.offer(HikMsgType.REPORT_NODE_REQ);
                        }
                        xMoveFlag = InitCache.realtimeStatus.getX() - InitCache.vehicleTargetInfo.getX();
                        yMoveFlag = InitCache.realtimeStatus.getY() - InitCache.vehicleTargetInfo.getY();
                        if (xMoveFlag == 0 && yMoveFlag == 0) {
                            resetMoveInfo();
                        }
                    }
                } else {
                    resetMoveInfo();
                }
            }
        }, 0L, 60, TimeUnit.MILLISECONDS);
    }

    /**
     * 重置移动信息
     */
    private static void resetMoveInfo() {
        // 目标状态重置
        InitCache.vehicleTargetInfo.setX(null);
        InitCache.vehicleTargetInfo.setY(null);
        startMove = false;
    }
}
